﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

/// <summary>
/// This is a set of static classes to define appropriate extension methods
/// for monadic parsing.
/// </summary>
namespace WaywardGamers.KParser.Monad
{
    /// <summary>
    /// The parser takes the token stream and uses the information provided
    /// to build a new message describing the full event that the chat line
    /// shows.
    /// </summary>
    internal static class Parser
    {
        #region Core functions that know the underlying parser representation
        /// <summary>
        /// This function always fails (consumes no input), regardless of input.
        /// </summary>
        /// <typeparam name="T">The type of result object generated.</typeparam>
        /// <returns>Returns a new empty Consumed object where none of the input was consumed,
        /// but the input position is marked.</returns>
        internal static P<TokenTuple, T> Fail<T>(string message)
        {
            return input =>
                new Consumed<TokenTuple, T>(
                    false,
                    new ParseResult<TokenTuple, T>(
                        new ErrorInfo(input.Position, Enumerable.Empty<string>(), message)));
        }

        /// <summary>
        /// Return(x) is a parser that always succeeds and returns a Consumed object
        /// containing a ParseResult of type T that contains x, without consuming
        /// any input.
        /// </summary>
        /// <typeparam name="T">The type of result object generated.</typeparam>
        /// <param name="x">The result object generated.</param>
        /// <returns>Always returns a Consumed object containing a ParseResult
        /// built with the provided result object.</returns>
        internal static P<TokenTuple, T> Return<T>(T x)
        {
            return input =>
                new Consumed<TokenTuple, T>(
                    false,
                    new ParseResult<TokenTuple, T>(
                        x,
                        input,
                        new ErrorInfo(input.Position)));
        }

        /// <summary>
        /// This function runs parser p1 on the input.  If it fails, return
        /// null.  If it succeeds, run the provided function on the result
        /// of the initial function.
        /// </summary>
        /// <typeparam name="T">The type of result object generated by the first function.</typeparam>
        /// <typeparam name="U">The type of result object generated by the second function.</typeparam>
        /// <param name="p1">The initial parser to run.</param>
        /// <param name="f">The function defining the second parser to run.</param>
        /// <returns>Returns the completely constructed parser.</returns>
        internal static P<TokenTuple, U> Then<T, U>(this P<TokenTuple, T> p1, Func<T, P<TokenTuple, U>> f)
        {
            return input =>
            {
                Consumed<TokenTuple, T> consumed1 = p1(input);

                if (consumed1.ParseResult.Succeeded)
                {
                    Consumed<TokenTuple, U> consumed2 = f(consumed1.ParseResult.Result)(consumed1.ParseResult.RemainingInput);
                    return new Consumed<TokenTuple, U>(
                           consumed1.HasConsumedInput || consumed2.HasConsumedInput,
                           consumed2.HasConsumedInput
                               ? consumed2.ParseResult
                               : consumed2.ParseResult.MergeError(consumed1.ParseResult.ErrorInfo));
                }
                else
                {
                    return new Consumed<TokenTuple, U>(
                        consumed1.HasConsumedInput,
                        new ParseResult<TokenTuple, U>(consumed1.ParseResult.ErrorInfo));
                }
            };
        }

        /// <summary>
        /// Then_ is a variant on Then.  It runs p1 predicate on the input,
        /// discards the results, and runs p2 on the result of the p1 function.
        /// </summary>
        /// <typeparam name="T">The type of result object generated by the first function.</typeparam>
        /// <typeparam name="U">The type of result object generated by the second function.</typeparam>
        /// <param name="p1">The first parser to run.</param>
        /// <param name="p2">The second parser to run.</param>
        /// <returns>Returns the combined parser function.</returns>
        internal static P<TokenTuple, U> Then_<T, U>(this P<TokenTuple, T> p1, P<TokenTuple, U> p2)
        {
            return p1.Then<T, U>(dummy => p2);
        }


        /// <summary>
        /// p1.Or(p2) tries p1, but if it fails without consuming input, runs p2 instead.
        /// </summary>
        /// <typeparam name="T">The type of result object generated.</typeparam>
        /// <param name="p1">The first parser to run.</param>
        /// <param name="p2">The second parser to run.</param>
        /// <returns>Returns the combined parser function.</returns>
        internal static P<TokenTuple, T> Or<T>(this P<TokenTuple, T> p1, P<TokenTuple, T> p2)
        {
            return input =>
            {
                Consumed<TokenTuple, T> consumed1 = p1(input);

                if (consumed1.ParseResult.Succeeded || consumed1.HasConsumedInput)
                {
                    return consumed1;
                }
                else
                {
                    Consumed<TokenTuple, T> consumed2 = p2(input);

                    if (consumed2.HasConsumedInput)
                        return consumed2;

                    return new Consumed<TokenTuple, T>(
                        consumed2.HasConsumedInput,
                        consumed2.ParseResult.MergeError(consumed1.ParseResult.ErrorInfo));
                }
            };
        }

        /// <summary>
        /// Satisfy(predicate) succeeds only if the predicate, when applied to
        /// a Token, returns true.  If it succeeds, return the Token that passed.
        /// </summary>
        /// <param name="pred">The predicate comparitor.</param>
        /// <returns></returns>
        internal static P<TokenTuple, TokenTuple> Satisfy(Predicate<TokenTuple> pred)
        {
            return input =>
            {
                // If we're past the end of the input, can't comply, fail.
                if (input.Position >= input.Input.Count)
                {
                    return new Consumed<TokenTuple, TokenTuple>(
                        false,
                        new ParseResult<TokenTuple, TokenTuple>(
                            new ErrorInfo(
                                input.Position,
                                Enumerable.Empty<string>(),
                                "unexpected end of input")));
                }
                // If predicate fails, return fail result.
                else if (!pred(input.Input[input.Position]))
                {
                    return new Consumed<TokenTuple, TokenTuple>(
                        false,
                        new ParseResult<TokenTuple, TokenTuple>(
                            new ErrorInfo(
                                input.Position,
                                Enumerable.Empty<string>(),
                                "unexpected string '" + input.Input[input.Position] + "'")));
                }
                // Otherwise return new Consumed result.
                else
                {
                    return new Consumed<TokenTuple, TokenTuple>(
                        true,
                        new ParseResult<TokenTuple, TokenTuple>(
                            input.Input[input.Position],
                            new ParserState<TokenTuple>(
                                input.Position + 1,
                                input.Input),
                            new ErrorInfo(input.Position + 1)));
                }
            };
        }

        /// <summary>
        /// Item() consumes the first element of the input string list and returns it as a result
        /// </summary>
        /// <returns></returns>
        internal static P<TokenTuple, TokenTuple> Item()
        {
            return Satisfy(c => true).Tag("any string");
        }

        #endregion

        #region Other helpful support functions
        /// <summary>
        /// Add a label to the state of any parse result.
        /// p.Tag(label) makes it so if p fails without consuming input, the error "expected label" occurs
        /// </summary>
        /// <typeparam name="T">The type of the result object.</typeparam>
        /// <param name="p">The predicate this is extending.</param>
        /// <param name="label">A string to use to tag the resulting parse state.</param>
        /// <returns></returns>
        internal static P<TokenTuple, T> Tag<T>(this P<TokenTuple, T> p, string label)
        {
            return input => p(input).Tag(label);
        }

        internal static P<TokenTuple, TokenType> Literal(
            List<TokenTuple> toParse, List<TokenTuple> consumed, TokenType result)
        {
            if (consumed == null)
                consumed = new List<TokenTuple>();

            if (toParse.Count == 0)
                return Parser.Return(result);
            else
                return Satisfy(c => c == toParse.First())
                    .Then_(Literal(toParse.Skip(1).ToList(), consumed, result));
        }


        #endregion

        #region Parser

        internal static Message Parse(List<TokenTuple> tokenList)
        {
            return new Message();
        }

        #endregion

    }




    /// <summary>
    /// The parser takes the token stream and uses the information provided
    /// to build a new message describing the full event that the chat line
    /// shows.
    /// </summary>
    //internal static class Parser1
    //{
    //    #region Core functions that know the underlying parser representation
    //    /// <summary>
    //    /// This function always returns null, regardless of input.
    //    /// </summary>
    //    /// <typeparam name="T"></typeparam>
    //    /// <returns></returns>
    //    internal static P<T> Fail<T>()
    //    {
    //        return input => null;
    //    }

    //    /// <summary>
    //    /// This function returns a fixed value x as part of a new
    //    /// ParseResult combining x with input.
    //    /// </summary>
    //    /// <typeparam name="T"></typeparam>
    //    /// <param name="x"></param>
    //    /// <returns></returns>
    //    internal static P<T> Return<T>(T x)
    //    {
    //        return input => new ParseResult<T>(x, input);
    //    }

    //    /// <summary>
    //    /// This function runs parser p1 on the input.  If it fails, return
    //    /// null.  If it succeeds, run the provided function on the result
    //    /// of the initial function.
    //    /// </summary>
    //    /// <typeparam name="T"></typeparam>
    //    /// <typeparam name="U"></typeparam>
    //    /// <param name="p1"></param>
    //    /// <param name="f"></param>
    //    /// <returns></returns>
    //    internal static P<U> Then<T, U>(this P<T> p1, Func<T, P<U>> f)
    //    {
    //        return input =>
    //        {
    //            ParseResult<T> result1 = p1(input);

    //            if (result1 == null)
    //                return null;
    //            else
    //                return f(result1.Result)(result1.RemainingInput);
    //        };
    //    }

    //    /// <summary>
    //    /// Then_ is a variant on Then.  It runs p1 predicate on the input,
    //    /// discards the results, and runs p2 on the result of the p1 function.
    //    /// </summary>
    //    /// <typeparam name="T"></typeparam>
    //    /// <typeparam name="U"></typeparam>
    //    /// <param name="p1"></param>
    //    /// <param name="p2"></param>
    //    /// <returns></returns>
    //    internal static P<U> Then_<T, U>(this P<T> p1, P<U> p2)
    //    {
    //        return p1.Then(dummy => p2);
    //    }

    //    /// <summary>
    //    /// This parser runs function p1.  If it succeeds, it returns
    //    /// the result of p1.  If it fails it runs and returns the
    //    /// result of p2.
    //    /// </summary>
    //    /// <typeparam name="T"></typeparam>
    //    /// <param name="p1"></param>
    //    /// <param name="p2"></param>
    //    /// <returns></returns>
    //    internal static P<T> Or<T>(this P<T> p1, P<T> p2)
    //    {
    //        return input =>
    //        {
    //            ParseResult<T> result1 = p1(input);

    //            if (result1 == null)
    //                return p2(input);
    //            else
    //                return result1;
    //        };
    //    }

    //    /// <summary>
    //    /// Item consumes the first object of the provided input and returns
    //    /// that as the result.
    //    /// </summary>
    //    /// <returns></returns>
    //    internal static P<Token> Item()
    //    {
    //        return input =>
    //        {
    //            if ((input == null) || (input.Count == 0))
    //                return null;

    //            return new ParseResult<Token>(input[0], input.Skip(1).ToList<Token>());
    //        };
    //    }

    //    /// <summary>
    //    /// Satisfy checks whether the first Item to be parsed satisfies the
    //    /// provided predicate check.  If it does, it returns the Item.
    //    /// </summary>
    //    /// <returns></returns>
    //    internal static P<Token> Satisfy(Predicate<Token> pred)
    //    {
    //        return Item().Then(s => pred(s) ? Parser.Return(s) : Parser.Fail<Token>());
    //    }
    //    #endregion

    //    #region Parser
    //    internal static Message Parse(List<Token> input)
    //    {
    //        return null;
    //    }
    //    #endregion

    //}
}
